home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume25 / pty4 / part09 < prev    next >
Encoding:
Text File  |  1992-02-18  |  26.0 KB  |  543 lines

  1. Newsgroups: comp.sources.unix
  2. From: brnstnd@nyu.edu (Dan Bernstein)
  3. Subject: v25i135: Generalized interface to pseudo-tty devices, Part09/09
  4. Sender: unix-sources-moderator@pa.dec.com
  5. Approved: vixie@pa.dec.com
  6.  
  7. Submitted-By: brnstnd@nyu.edu (Dan Bernstein)
  8. Posting-Number: Volume 25, Issue 135
  9. Archive-Name: pty4/part09
  10.  
  11. #! /bin/sh
  12. # This is a shell archive.  Remove anything before this line, then unpack
  13. # it by saving it into a file and typing "sh file".  To overwrite existing
  14. # files, type "sh file -c".  You can also feed this as standard input via
  15. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  16. # will see the following message at the end:
  17. #        "End of archive 9 (of 9)."
  18. # Contents:  JOBCTRL.draft3
  19. # Wrapped by vixie@cognition.pa.dec.com on Wed Feb 19 13:35:07 1992
  20. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  21. if test -f 'JOBCTRL.draft3' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'JOBCTRL.draft3'\"
  23. else
  24. echo shar: Extracting \"'JOBCTRL.draft3'\" \(24823 characters\)
  25. sed "s/^X//" >'JOBCTRL.draft3' <<'END_OF_FILE'
  26. How to add job control to a UNIX system
  27. Daniel J. Bernstein
  28. draft 3
  29. X8/3/91
  30. X
  31. X
  32. Abstract
  33. X
  34. We describe in detail the steps necessary to add BSD-style job control
  35. to any UNIX system. In place of the BSD and POSIX rules for controlling
  36. ttys, sessions, and process groups, we propose a very simple yet secure
  37. mechanism for manipulating process groups alone. This mechanism can also
  38. be added to existing BSD systems to provide an alternate, easier-to-use
  39. programming interface.
  40. X
  41. X
  42. X1. Introduction
  43. X
  44. Sections 2 through 6 describe selected portions of BSD 4.2 and 4.3 job
  45. control. Omitted is any mention of controlling ttys, POSIX sessions,
  46. getpgrp() and setpgrp(), TIOCGPGRP and TIOCSPGRP, tcgetpgrp() and
  47. tcsetpgrp(), TIOCNOTTY, setsid(), TIOCSCTTY, setpgid(), open() with or
  48. without O_NOCTTY, and the relations between all of those and the rest of
  49. the job control system, because it turns out that none of that is
  50. necessary to provide job control. The attitude in these sections is that
  51. of someone faced with a System V variant or a new UNIX system (e.g.,
  52. MINIX) with no job control facilities in the first place, perhaps
  53. without even the concept of a controlling tty; the important question is
  54. how little work is necessary to add job control features.
  55. X
  56. Section 7 describes my new, secure, extremely simple job control
  57. programming interface [1]. (The interface was inspired by a comment from
  58. Chris Torek. It was modified slightly in response to criticism by
  59. John Carr. It is dedicated to Marc Teitelbaum.) The interface is enough
  60. to let programmers implement a job control shell or any other job
  61. control-cognizant applications. It solves all the problems that POSIX
  62. sessions were meant to solve, but it is much, much simpler, and can be
  63. added to a system with a minimum of effort. It can even be added to a
  64. BSD system, as discussed in section 8---it does not interfere with the
  65. old job control model in any way. This will give programmers a choice
  66. between the older, more complicated interface and this new, easy-to-use
  67. interface.
  68. X
  69. Section 9 lists several job control programming techniques. Finally,
  70. section 10, again from the point of view of a system without job
  71. control, lists some common macros and similar cpp-level extensions
  72. which make job control programs easier to port.
  73. X
  74. X
  75. X2. New kernel structures
  76. X
  77. XEach process ``is a member of'' a process group. In other words, there's
  78. a p_pgrp integer inside struct proc. init starts out in process group 0.
  79. Process groups remain the same across fork() and exec().
  80. X
  81. XEach tty has a ``foreground'' process group. In other words, there's a
  82. t_pgrp integer inside struct tty. (Systems where ttys are implemented
  83. differently, e.g., via streams, will have to store this information
  84. somewhere else.) A tty opened for the first time has t_pgrp set to 0.
  85. XEach tty also has one extra keyboard character, the suspend character,
  86. with a default of 26 (^Z).
  87. X
  88. There is a new process state (i.e., p_stat value): SSTOP. (ps usually
  89. reports this state as T.) When a process is in this state, it gets no
  90. CPU time. All signals are blocked until it leaves the state. Note that
  91. systems with some form of process tracing (e.g., ptrace(2)) already have
  92. SSTOP.
  93. X
  94. X
  95. X3. Signals
  96. X
  97. There are five new signals declared in <signal.h>: SIGSTOP (17), SIGTSTP
  98. X(18), SIGCONT (19), SIGTTIN (21), SIGTTOU (22). (The numbers in
  99. parentheses are the standard BSD values.) Any code which works with bit
  100. masks representing signals must be prepared to work with 32-bit masks.
  101. X
  102. The default action of a process receiving SIGSTOP, SIGTSTP, SIGTTIN, or
  103. SIGTTOU is to stop, i.e., enter the SSTOP state and, as detailed below,
  104. to generate a SIGCHLD. SIGSTOP cannot be blocked, caught, or ignored.
  105. X(``Blocking'' refers to any mechanism by which the receipt of a signal
  106. is deferred. BSD provides sigblock() and sigsetmask() to manipulate a
  107. bit mask of blocked signals. On systems without a similar mechanism,
  108. SIGSTOP obviously can't be blocked in the first place. What's important
  109. is tht SIGSTOP always take effect immediately.)
  110. X
  111. Any process which receives SIGCONT will continue, i.e., leave the SSTOP
  112. state; this is in addition to any signal handler installed. (Obviously
  113. the process cannot execute a signal handler if it's in the SSTOP state,
  114. receiving no CPU time!) SIGCONT cannot be blocked. A process is always
  115. able to send SIGCONT to any of its children, regardless of permission
  116. checks. (BSD actually lets you send SIGCONT to any descendant. Some
  117. popular BSD variants do not obey this rule.)
  118. X
  119. When a process enters the SSTOP state, it generates a SIGCHLD (aka
  120. SIGCLD) to its parent. There are several conflicting sets of semantics
  121. for SIGCHLD/SIGCLD (e.g., what happens when it's ignored? when are
  122. zombies created?) on various systems, none of which have any relevance
  123. to job control.
  124. X
  125. X
  126. X4. Waiting
  127. X
  128. When the parent, either upon receiving a SIGCHLD or at any other time,
  129. does a wait(), it will not see any stopped children---i.e., job control
  130. doesn't change the semantics of wait(). (Process tracing does, but that
  131. is also irrelevant to job control.) There is a new system call, wait2,
  132. which lets the parent see stopped children:
  133. X
  134. X  #include <sys/wait.h>
  135. X
  136. X  int wait2(status,options)
  137. X  int *status;
  138. X  int options;
  139. X
  140. X(In fact, BSD has a wait3() call instead of wait2(); the above call is
  141. the same as wait3(status,options,(struct rusage *) 0). See section 10
  142. for further details.) options is a bit field. You have to define two
  143. bits, WNOHANG and WUNTRACED, in <sys/wait.h>, for use as options.
  144. X
  145. Normally wait2() acts like wait(): it blocks waiting for a child to die
  146. and then returns the dead pid, or returns -1 immediately if there are no
  147. live children. If options includes WNOHANG, wait2() will return 0
  148. immediately instead of blocking. If options includes WUNTRACED, wait2()
  149. will return the pid of a stopped child as well as the pid of a dead
  150. child. (By far the most common options value is WNOHANG | WUNTRACED.)
  151. X
  152. As usual, when wait2() returns a pid, status says what's happened to
  153. that pid. This is a bit more complicated than before because status also
  154. has to tell the parent what happened if the child stopped. Here's the
  155. whole story: If the low 7 bits are all set, the child has in fact
  156. stopped. If none of those bits are set, the child has exited normally.
  157. Otherwise the child has been terminated by a signal, and those 7 bits
  158. say which signal it was. (If the 8th bit is set in that case, the child
  159. dumped core.) If the child has stopped, the 8th bit is 0, and the 8 bits
  160. after that say which signal (SIGTTOU, for instance) stopped the process.
  161. If the child has exited, the 8th bit is 0, and the 8 bits after that
  162. give its exit code mod 256.
  163. X
  164. X
  165. X5. Terminal-generated signals
  166. X
  167. When the interrupt character (typically ^C under BSD, DEL under System V)
  168. is typed on a terminal in cooked mode, if the terminal's foreground
  169. process group is non-zero, every process in that process group is sent
  170. SIGINT. Similarly, the quit character (typically ^\ under BSD) generates
  171. SIGQUIT. If a terminal is ``hung up'', it generates SIGHUP. Job control
  172. needs one extra signal so that the user can tell the current process to
  173. stop: namely, the suspend character mentioned above (typically ^Z),
  174. which generates SIGTSTP. Notice that if a user could set his tty's
  175. process group arbitrarily, he could send all sorts of signals to any
  176. processes in those process groups. So it is important for security that
  177. tty process groups be controlled.
  178. X
  179. The suspend character is the first user interface aspect of job control
  180. mentioned so far. Typically the processes stop (though they can catch
  181. SIGTSTP and do something else). A job-control shell then receives the
  182. SIGCHLD and, with wait2(), sees that its children have stopped. It can
  183. report this to the user and present a new prompt. The user can then
  184. start more processes, or, with an ``fg'' (foreground) command, tell the
  185. shell to send SIGCONT to the children so that they start up again.
  186. X
  187. Programs can inspect and set the suspend character with two new tty
  188. ioctls: TIOCGLTC and TIOCSLTC, both defined in <sys/ioctl.h>. In both
  189. cases the argument points to a ``struct ltchars'' (defined in the same
  190. place), which contains a char t_suspc specifying the suspend character.
  191. X
  192. As a matter of fact, under BSD there are several other local terminal
  193. characters (that's what ltchars stands for), notably t_dsuspc. The
  194. delayed suspend character (typically ^Y) is supposed to act like the
  195. suspend character but only when a process actually reads it. However,
  196. several operating system releases from Sun simply don't do this. They
  197. pass dsusp through like any other character. Given that almost nobody
  198. ever notices this bug, let alone complains about it, I don't think
  199. there's any point in bothering to implement the character.
  200. X
  201. X
  202. X6. I/O-generated signals
  203. X
  204. There's another side to the job control user interface: namely, several
  205. processes (or pipelines---in general, ``jobs'') can read and write the
  206. tty at once. The job-control shell places each pipeline into a separate
  207. process group, and when any job except the foreground job reads from the
  208. tty, it is stopped until the user decides to give it input. This is much
  209. more flexible than cutting background processes off from the tty
  210. permanently, as non-job-control shells do.
  211. X
  212. More precisely, if a process reads from a tty, and its process group is
  213. not the foreground process group of the tty, then its process group is
  214. sent a SIGTTIN signal. As an exception, if that process is blocking or
  215. ignoring SIGTTIN, no signal is generated. Instead, the read returns -1
  216. with errno of EIO. ``Reading'' here includes only read(), not the
  217. various tty ioctls which inspect tty structures; while there are some
  218. benefits of generating SIGTTIN for the latter, this turns out to be too
  219. restrictive for many applications. (There is an ioctl, TIOCSTI, which is
  220. also lumped with ``reading,'' but a full discussion of TIOCSTI would be
  221. too long for this paper. It's not an important enough ioctl to bother
  222. with.)
  223. X
  224. If a process writes to a tty, and its process group is not the
  225. foreground process group of the tty, then its process group is sent a
  226. SIGTTOU signal. As an exception, if that process is blocking or ignoring
  227. SIGTTOU, no signal is generated and it is allowed to produce output.
  228. This time, ``write'' includes not only write() but also any other
  229. operations which affect the tty in any way. (Under BSD there is a tty
  230. mode, LTOSTOP, which when disabled turns off TTOU for write() but not
  231. for other operations. This is not absolutely necessary, but if you have
  232. any free time you should implement stty tostop to turn LTOSTOP on and
  233. stty -tostop to turn it off. The internal interface is unimportant as
  234. long as the user can select his favorite behavior.)
  235. X
  236. None of the above apply to operations by a process in process group 0.
  237. Process group 0 must never, ever, be sent I/O-generated signals. The
  238. simplest course of action here is to let all operations from process
  239. group 0 succeed. (What actually happens in this case isn't too
  240. important, as long as processes like getty can open a tty and start
  241. programs on the tty. Most BSD-derived systems set process group to pid
  242. when a process in process group 0 opens a tty; this behavior is not
  243. necessary. Note that if a process in process group 0 reads from a tty
  244. while a shell is still reading from it, the two read()s will compete for
  245. terminal input.)
  246. X
  247. Notice that if a process can join an arbitrary process group, it can
  248. cause SIGTTOU and SIGTTIN to be sent to other process. So it's important
  249. for security that processes' process groups be controlled.
  250. X
  251. Be careful in implementing I/O-generated signals that you test
  252. repeatedly for the right process group. The process could easily receive
  253. SIGCONT while the tty is in a different group. In that case it should
  254. immediately stop the process group again (without even executing a
  255. SIGCONT handler!), generate another SIGCHLD, and wait for the next
  256. SIGCONT. This can repeat any number of times. Only when the tty is in
  257. the right process group should the operation succeed.
  258. X
  259. X
  260. X7. A new, secure, simple job control programming interface
  261. X
  262. The process group calls described in this section are, unlike the job
  263. control features described in sections 2 through 6, not part of BSD,
  264. though they do not interfere with BSD. There are a total of three calls
  265. which manipulate process groups: tcnewpgrp(), settpgrp(), tctpgrp().
  266. Throughout this section, fdtty is a file descriptor pointing to a
  267. terminal.
  268. X
  269. If fdtty has write access, tcnewpgrp(fdtty) should allocate an unused
  270. process group and set the terminal's foreground process group to that
  271. new process group. This is a write operation and should produce SIGTTOU
  272. if this process is not in the foreground (and is not ignoring the
  273. signal, etc.). tcnewpgrp returns 0 on success, -1 with errno ENOTTY if
  274. fdtty is not a terminal, -1 with errno EBADF if fdtty is not open for
  275. writing.
  276. X
  277. If fdtty has read access, settpgrp(fdtty) should set this process's
  278. process group to the foreground process group of the terminal. As a
  279. special case, settpgrp(-1) sets this process's process group to 0, so
  280. that it is exempt from job control. The latter is redundant---a process
  281. can just as easily create a process group for itself, fork, and hide the
  282. child away inside that group---but convenient. settpgrp returns 0 on
  283. success, -1 with errno ENOTTY if fdtty is not -1 and not a terminal, -1
  284. with errno EBADF if fdtty is not open for reading.
  285. X
  286. If fdtty has write access, and pid is the current process or a child of
  287. the current process, tctpgrp(fdtty,pid) should set the terminal's
  288. foreground process group to the process group of pid. This is a write
  289. operation. You may want to allow pid to be any descendant of the current
  290. process (under BSD this simplifies the implementation), but this is not
  291. necessary for a job control shell, and nobody is going to depend on that
  292. behavior. tctpgrp returns 0 on success, -1 with errno ENOTTY if fdtty is
  293. not a terminal, -1 with errno ESRCH if pid does not exist, -1 with errno
  294. XEPERM if pid exists but is not a child/descendant, -1 with errno EBADF
  295. if fdtty is not open for reading.
  296. X
  297. To implement tcnewpgrp() you need to set up a table (I recommend a
  298. chained hash table) of structures containing process group number and
  299. reference count. The reference count is the total number of processes
  300. and ttys with that process group. tcnewpgrp() can then search for a
  301. process group not in the table. The range of process group numbers is
  302. not important; a good choice for BSD systems is 32801-65000. However, it
  303. is important that there be more process groups available than the maximum
  304. possible number of ttys and pids in use at once.
  305. X
  306. Whenever a process is created, the reference count for its process group
  307. X(if that group is not 0) must be incremented; whenever a process dies,
  308. the reference count for its process group (if that group is not 0) must
  309. be decremented; whenever a process changes process groups (e.g., via
  310. settpgrp()), the reference counts for old and new groups must be set
  311. appropriately; and whenever a tty changes process groups (e.g., via
  312. tcnewpgrp() or tctpgrp()), the reference counts must also be set
  313. appropriately. That's it.
  314. X
  315. A different implementation strategy has been suggested by John Carr: the
  316. system can simply assign group numbers in increasing order starting from
  317. boot time. If, for instance, a process group has 64 bits, and there are
  318. at most a billion process group manipulations per second, it will be
  319. more than 584 years before the numbers can repeat. Naturally, system
  320. administrators should keep a close eye on recently allocated process
  321. groups, and be prepared to bring the system down for maintenance as soon
  322. as there is any risk of repetition.
  323. X
  324. These three process group manipulation calls do not allow any abuse. To
  325. set a terminal to someone else's (nonzero) process group with tctpgrp(),
  326. an attacker would need a child process already in the group. But to put
  327. a process into someone else's (nonzero) process group with settpgrp(),
  328. an attacker would already need access to a tty with that group! There's
  329. no way to break into this circle. tcnewpgrp() is useless for attacks
  330. since it does not let an attacker join an existing group. Hence the
  331. system is secure. Together with the basic job control features outlined
  332. in sections 2 through 6, this provides a complete, usable job control
  333. system.
  334. X
  335. XFor comparison, BSD job control involves controlling ttys, and has six
  336. interface functions beyond the mechanisms mentioned in sections 2
  337. through 6: open() (of a tty), setpgrp(), getpgrp(), the TIOCGPGRP ioctl,
  338. the TIOCSPGRP ioctl, and the TIOCNOTTY ioctl. Controlling terminals
  339. affect the entire job control system and make everything harder to
  340. program and use.
  341. X
  342. POSIX job control is even worse: it includes not only the entire
  343. complexity of the BSD interface, but it has ``sessions'' with effects
  344. even more pervasive than those of controlling terminals. (For instance,
  345. a process can only be stopped if its parent is in the *same* session but
  346. a *different* process group.)
  347. X
  348. X
  349. X8. Implementing the new job control interface in a BSD system
  350. X
  351. tcnewpgrp() requires kernel changes on any system; current systems do
  352. not recognize a range of process groups to be dynamically allocated to
  353. ttys. It also allows a style of job-control programming somewhat
  354. different from the usual BSD style. However, settpgrp() and tctpgrp()
  355. can be implemented as library routines under BSD. Here they are:
  356. X
  357. X   int settpgrp(fdtty)
  358. X   int fdtty;
  359. X   {
  360. X    int pgrp = 0;
  361. X    if (fdtty != -1)
  362. X      if (ioctl(fdtty,TIOCGPGRP,&pgrp) == -1)
  363. X        return -1;
  364. X    return setpgrp(0,pgrp);
  365. X   }
  366. X
  367. X   int tctpgrp(fdtty,pid)
  368. X   int fdtty;
  369. X   int pid;
  370. X   {
  371. X    int pgrp;
  372. X    if ((pgrp = getpgrp(pid)) == -1)
  373. X      return -1;
  374. X    return ioctl(fdtty,TIOCSPGRP,&pgrp);
  375. X   }
  376. X
  377. Note that this interface doesn't interact with controlling ttys in any
  378. way. Unfortunately, controlling ttys sometimes force their own
  379. interactions, and a job control application which manipulates ttys (as
  380. opposed to a shell, which merely runs under a single tty) should still
  381. be aware of the old controlling tty rules. The same is true in far
  382. greater measure under POSIX---you simply cannot ignore sessions, because
  383. you will open up rather large security holes if you leave all processes
  384. in the same session. Put simply, the POSIX standard forces system code
  385. to manipulate sessions for its health.
  386. X
  387. X
  388. X9. Programming common operations with the new job control interface
  389. X
  390. XForking a pipeline in a job-control shell: The shell starts with
  391. tcnewpgrp(fdtty), so that the tty is in the new process group before
  392. there are even any children. (That's the basic difference between the
  393. BSD and POSIX models and this one.) It then forks each process in the
  394. pipeline. Each process does settpgrp(fdtty), thus joining the new
  395. process group, before it exec()s the appropriate program. Note that to
  396. avoid races the shell should block SIGCHLD while it's spawning children.
  397. X
  398. Handling a stopped child process: When the shell sees that a pipeline
  399. has stopped or exited, it does tctpgrp(fdtty,getpid()) to set the tty to
  400. its own process group. Note that it has to ignore SIGTTOU during this
  401. operation. To resume the pipeline it does tctpgrp(fdtty,pid) where pid
  402. is any one of the child processes, then sends SIGCONT to the process
  403. group.
  404. X
  405. Starting a process under a new tty: When, for instance, telnetd or
  406. init/getty or another program in process group 0 wants to grab a tty, it
  407. opens the tty and forks a child process. The child does tcnewpgrp(fdtty)
  408. to give the tty a real process group, then settpgrp(fdtty) to place
  409. itself into the foreground.
  410. X
  411. Changing ttys: Despite what POSIX would have you believe with its
  412. session straitjacket rules, people do run programs all the time under a
  413. different tty from the shell. The most common example in BSD is probably
  414. the script program; other examples are emacs, screen, pty, mtty, atty.
  415. XFortunately, exactly the same procedure works as in the previous
  416. example.
  417. X
  418. Dissociating a daemon: Note that dissociating from a tty is a
  419. controlling-terminal concept. However, most daemons do want to place
  420. themselves into process groups of their own, so that they are not
  421. affected by job-control signals. This can be handled in several ways,
  422. but by far the easiest is settpgrp(-1) to join process group 0. (Note
  423. that under BSD there is no reliable way to dissociate from a controlling
  424. tty---the TIOCEXCL ioctl can prevent dissociation. That is not the mark
  425. of a clean interface.)
  426. X
  427. XForcing oneself into the foreground: Most programs which manipulate the
  428. tty, usually so that they can run in character mode, don't work
  429. correctly with job control. The usual sequence after startup is this:
  430. read tty modes; write new tty modes including noecho and cbreak. The
  431. problem is that the process could be in the background when it reads the
  432. tty modes---a different program, which itself changes the tty modes to
  433. something strange, could be in the foreground. This process will read
  434. the strange modes, then stop when it tries to set the modes. Later it is
  435. restarted and runs without trouble---but when it exits, it will
  436. X``restore'' the tty to those strange modes it started with. To avoid
  437. this bug, processes which manipulate the tty should force themselves
  438. into the foreground before reading or writing anything. An easy way to
  439. do this is tctpgrp(getpid()), with the default SIGTTOU handler. Note
  440. that the program should also do this upon continuing after a stop---
  441. otherwise it might make the same mistake of reading modes before it
  442. knows it's in the foreground.
  443. X
  444. X
  445. X10. Helpful extensions to the job control system
  446. X
  447. There are several steps you can take which don't extend the job control
  448. interface but which do make job control programs more portable or easier
  449. to read.
  450. X
  451. As noted above, BSD has a wait3() call instead of wait2(). It is called
  452. as follows:
  453. X
  454. X  #include <sys/wait.h>
  455. X  #include <sys/time.h>
  456. X  #include <sys/resource.h>
  457. X
  458. X  int wait3(status,options,rusage)
  459. X  int *status;
  460. X  int options;
  461. X  struct rusage *rusage;
  462. X
  463. If rusage is NULL, this is just like wait2(). <sys/time.h> can simply
  464. X#include <time.h>. (Under BSD it defines several system time structures,
  465. like struct timeval.) <sys/resource.h> doesn't need to provide any
  466. information other than a definition of struct rusage. (Under BSD, if a
  467. child exits and the parent provides a non-NULL rusage pointer to
  468. wait3(), the structure is filled in with information about the resources
  469. used by the child [and its children, and so on]. For instance,
  470. ru_nsignals is the number of signals received. This is very open-ended
  471. and absolutely irrelevant to job control.) If you are adding job control
  472. to a system without it and want to provide the wait3() call, just define
  473. struct rusage { int dummy; }. While a job-control shell can make good
  474. use of resource information, most uses of wait3() really don't need the
  475. third argument. However, there are enough programs which include
  476. X<sys/time.h> and <sys/resource.h> and use wait3() that it is worthwhile
  477. to provide the extra interface.
  478. X
  479. Another extension is to define a ``union wait'' type in <sys/wait.h>
  480. with an ``int w_status'' member. At some point BSD left the beaten path
  481. and decided that wait() should use a union wait instead of an int to
  482. return status information. This decision is generally regarded as a
  483. mistake if only because it severely hampers portability, but there are
  484. quite a few programs which depend on it, and there's no harm in
  485. supporting it.
  486. X
  487. More useful is to define a set of macros which extract information from
  488. a wait status. (Under BSD, union wait actually contains structure
  489. members which encode the same information. However, the macros are
  490. easier to use and support.) Here are the important ones:
  491. X
  492. X   #define WIFSTOPPED(s) (((s) & 0177) == 0177)
  493. X   #define WIFEXITED(s) (!((s) & 0177))
  494. X   #define WIFSIGNALED(s) (0176 > (unsigned) (((s) & 0177) - 1))
  495. X   #define WSTOPSIG(s) ((s) >> 8) /* only defined if WIFSTOPPED */
  496. X   #define WEXITSTATUS(s) ((s) >> 8) /* only defined if WIFEXITED */
  497. X   #define WTERMSIG(s) ((s) & 0177) /* only defined if WIFSIGNALED */
  498. X   #define WCOREDUMP(s) ((s) & 0200) /* only defined if WIFSIGNALED */
  499. X
  500. On some 16-bit machines the >> 8 may have to be changed, or (s) may have
  501. to be cast to unsigned. These macros are meant to be applied to an int,
  502. not a union wait; most compilers will do the right thing anyway, but be
  503. careful.
  504. X
  505. X
  506. Acknowledgments
  507. X
  508. Thanks to Chris Torek, Christos S. Zoulas, and David J. MacKenzie for
  509. their comments. Thanks also to John F. Haugh for a series of questions
  510. which pointed out that, somewhere in this paper, I should emphasize that
  511. an ``unused'' process group is one which doesn't appear in any t_pgrp or
  512. p_pgrp. (There, I said it.)
  513. X
  514. X
  515. References
  516. X
  517. X[1] D. J. Bernstein, ``A new, secure, extremely simple job control
  518. interface,'' article <18072.Jul1707.06.4191@kramden.acf.nyu.edu>,
  519. comp.unix.wizards, July 1991.
  520. END_OF_FILE
  521. if test 24823 -ne `wc -c <'JOBCTRL.draft3'`; then
  522.     echo shar: \"'JOBCTRL.draft3'\" unpacked with wrong size!
  523. fi
  524. # end of 'JOBCTRL.draft3'
  525. fi
  526. echo shar: End of archive 9 \(of 9\).
  527. cp /dev/null ark9isdone
  528. MISSING=""
  529. for I in 1 2 3 4 5 6 7 8 9 ; do
  530.     if test ! -f ark${I}isdone ; then
  531.     MISSING="${MISSING} ${I}"
  532.     fi
  533. done
  534. if test "${MISSING}" = "" ; then
  535.     echo You have unpacked all 9 archives.
  536.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  537. else
  538.     echo You still need to unpack the following archives:
  539.     echo "        " ${MISSING}
  540. fi
  541. ##  End of shell archive.
  542. exit 0
  543.